﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace Krilib.Control.Surface.MessageObjects
{
    public class Text : MessageObject
    {
        private string _value;
        public string Value
        {
            get { return _value; }
            set
            {
                _value = value;
                Bridge.MainForm.RefreshTL(true);
            }
        }

        public Color _foreColor;
        public Color ForeColor
        {
            get { return _foreColor; }
            set
            {
                _foreColor = value;
                Bridge.MainForm.RefreshTL(false);
            }
        }

        protected virtual Font InternalFont
        {
            get { return Bridge.Config.Font; }
        }

        public Text()
            : this(String.Empty) { }

        public Text(string text)
            : this(text, Bridge.Config.ForeColor) { }

        public Text(string text, Color forecolor)
        {
            _value = text;
            _foreColor = forecolor;
        }

        object dsplitsLocker = new object();
        List<string> drawSplits = new List<string>();
        public override void Composite(Compositor compz)
        {
            if (Value == null || Value == String.Empty) return;
            IntPtr hWnd = (IntPtr)((Form)Bridge.MainForm).Invoke(new Func<IntPtr>(() => { return ((Form)Bridge.MainForm).Handle; }));
            using (Graphics g = Graphics.FromHwnd(hWnd))
            {
                lock (dsplitsLocker)
                {
                    //コンポジション
                    drawSplits.Clear();
                    string buf = (string)Value.Clone();
                    Size s = new Size();
                    while (buf != String.Empty)
                    {
                        //文字サイズの算出
                        s = MStr(g, buf);
                        if (s.Width < compz.ClientWidth - compz.CurrentXPosition)
                        {
                            compz.SetCurrentLineHeight(s.Height);
                            drawSplits.Add(buf);
                            compz.CurrentXPosition = compz.CurrentXPosition + s.Width;
                            break;
                        }
                        else
                        {
                            //描画可能な長さを計算
                            int drawlen = GetSuitableLength(g, 0, buf.Length, compz.ClientWidth - compz.CurrentXPosition, buf);
                            //文字処理
                            string cur = buf.Substring(0, drawlen);
                            buf = buf.Remove(0, drawlen);
                            s = MStr(g, cur);
                            drawSplits.Add(cur);
                            //描画位置のレジューム
                            compz.SetCurrentLineHeight(s.Height);
                            compz.MoveNewLine();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 指定された幅に収まる文字列長を返します。
        /// </summary>
        /// <param name="g">使用するグラフィックス</param>
        /// <param name="subidx">subidx(0を指定してください)</param>
        /// <param name="diff">diff(文字全体の長さを指定してください)</param>
        /// <param name="targetWidth">目標幅</param>
        /// <param name="complete">全体の文字列</param>
        /// <returns></returns>
        protected int GetSuitableLength(Graphics g, int subidx, int diff, int targetWidth, string complete)
        {
            if (targetWidth < 5)
                return 1;
            bool overflow = diff + subidx > complete.Length;
            if (subidx == 0 && diff == 0) return 1;
            Size cSize = new Size();
            if (!overflow)
                cSize = MStr(g, complete.Substring(0, subidx + diff));
            if (cSize.Width > targetWidth)
            {
                if (diff + subidx == 1)
                    return 1;
                //too long
                if (diff == 1)
                    return subidx + diff - 1;
                return GetSuitableLength(g, subidx, diff / 2, targetWidth, complete);
            }
            else
            {
                if (overflow)
                    return complete.Length;
                //too short
                if (complete.Length == subidx) return subidx;
                return GetSuitableLength(g, subidx + diff, diff > 1 ? diff / 2 : 1, targetWidth, complete);
            }
        }

        object cbLocker = new object();
        Rectangle[] cBounds = null;
        public override void Draw(Graphics g, CompositeDescriptor desc)
        {
            if (drawSplits.Count == 0)
                return;
            lock (dsplitsLocker)
            {
                int cnt = 0;
                int ch = 0;
                Rectangle[] bounds = new Rectangle[drawSplits.Count];
                int spoint = desc.StartXPos;
                foreach (var s in drawSplits)
                {
                    //描画位置の算出
                    int w = TextRenderer.MeasureText(g, s, InternalFont, new Size(), TextFormatFlags.Left | TextFormatFlags.SingleLine |
                        TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix).Width;
                    var bound = new Rectangle(
                        desc.LeftTop.X + spoint, desc.LeftTop.Y + ch,
                        w, desc.Heights[cnt]);
                    bounds[cnt] = bound;
                    TextRenderer.DrawText(
                        g,
                        s,
                        InternalFont,
                        bound,
                        ForeColor,
                        TextFormatFlags.Left | TextFormatFlags.SingleLine |
                        TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding |
                        TextFormatFlags.NoPrefix);
                    ch += desc.Heights[cnt];
                    cnt++;
                    spoint = 0;
                }
                lock (cbLocker)
                {
                    cBounds = bounds;
                }
            }
        }

        public override bool HitTest(Point position)
        {
            if (cBounds == null || cBounds.Length == 0)
                return false;
            lock (cbLocker)
            {
                foreach (var r in cBounds)
                {
                    if (r.HitTest(position))
                        return true;                        
                }
            }
            return false;
        }

        public override void Release()
        {
            lock (cbLocker)
            {
                cBounds = null;
            }
        }
        
        /// <summary>
        /// Measure String Size
        /// </summary>
        protected Size MStr(Graphics g, string t)
        {
            return TextRenderer.MeasureText(g, t, InternalFont, new Size(), TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
        }
    }
}
